﻿using System;
using System.Collections.Generic;
using System.Text;
using EmbeddedSharp.Core;
using Zove.Runtime.Core;

namespace Zove.Runtime.Queries {
    /// <summary>
    /// Query parser
    /// </summary>
    public class QueryParser {
        /// <summary>
        /// Lexer
        /// </summary>
        protected Lexer Lexer;

        /// <summary>
        /// Source
        /// </summary>
        protected readonly string Source;

        /// <summary>
        /// Token
        /// </summary>
        protected Token Token;

        protected ILanguageNature LanguageNature { get; set; }

        /// <summary>
        /// Default constructor
        /// </summary>
        /// <param name="languageNature">the language nature</param>
        public QueryParser(ILanguageNature languageNature) {
            LanguageNature = languageNature;
        }

        /// <summary>
        /// Parse
        /// </summary>
        /// <returns>the query</returns>
        public Query Parse(string source) {
            var d = new Query(source, LanguageNature);
            using (Lexer = new Lexer(source)) {
                Token = Lexer.NextToken();

                while (Token.TokenType == TokenType.IDENTIFIER) {
                    switch (Token.Value) {
                        case ";":
                            break;
                        case "select":
                            d.Select = ParseExpressions();
                            break;
                        case "where":
                            d.Where = ParseWhere();
                            break;
                        case "from":
                            d.From = ParseFrom();
                            break;
                        case "assert":
                            d.Assert = ParseExpressions();
                            break;
                        case "import":
                            break;
                        default:
                            throw new Exception(
                                string.Format("Token is not as expected {0}",
                                              Token.Value));
                    }
                }
            }

            return d;
        }

        /// <summary>
        /// Parse expressions
        /// </summary>
        /// <returns>a list of expressions</returns>
        public List<string> ParseExpressions() {
            int braceCount = 0;
            var buff = new StringBuilder();
            var s = new List<string>();

            Token = Lexer.NextToken();
            while (!IsKeyword(Token.Value) && Token.TokenType != TokenType.End) {
                buff.Append(Token.Value);
                Token = Lexer.NextToken();
                switch (Token.TokenType) {
                    case TokenType.LeftBrace:
                        braceCount++;
                        break;
                    case TokenType.RightBrace:
                        braceCount--;
                        break;
                    case TokenType.Comma:
                        if (braceCount == 0) {
                            s.Add(buff.ToString());
                            Token = Lexer.NextToken();

                            buff = new StringBuilder();
                        }
                        break;
                    default:
                        break;
                }
            }
            s.Add(buff.ToString());
            if (braceCount != 0) {
                throw new Exception();
            }

            return s;
        }

        /// <summary>
        /// Check if the token is a keyword
        /// </summary>
        /// <param name="token">token</param>
        /// <returns>true if it is a keyword</returns>
        public static bool IsKeyword(string token) {
            return token == "where" || token == "select" || token == "from" || token == ";";
        }

        /// <summary>
        /// Parse the selection
        /// </summary>
        /// <returns></returns>
        public List<string> ParseSelect() {
            var buff = new StringBuilder();
            var s = new List<string>();

            Token = Lexer.NextToken();
            while (!IsKeyword(Token.Value)) {
                buff.Append(Token.Value);
                Token = Lexer.NextToken();
                if (Token.Value == ",") {
                    s.Add(buff.ToString());
                    Token = Lexer.NextToken();

                    buff = new StringBuilder();
                }
            }
            s.Add(buff.ToString());

            return s;
        }

        public Dictionary<string, string> ParseWhere() {
            List<string> where = ParseExpressions();
            var w = new Dictionary<string, string>();

            foreach (string s in where) {
                string[] p = s.Split('=');
                w.Add(p[0], p[1]);
            }

            return w;
        }

        public string ParseFrom() {
            Token = Lexer.NextToken();
            string v = Token.Value;
            Token = Lexer.NextToken();

            return v;
        }
    }
}